package com.xxh.auth.config;

import com.xxh.common.constant.AuthConstants;
import com.xxh.dataaccess.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.token.*;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;

import java.security.KeyPair;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 开启授权服务
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private ClientDetailsService clientDetailsService;

    @Autowired
    private AuthorizationCodeServices authorizationCodeServices;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    PasswordEncoder passwordEncoder;

    @Autowired
    private JwtAccessTokenConverter accessTokenConverter;

    @Autowired
    private WebResponseExceptionTranslator<OAuth2Exception> oauth2ResponseExceptionTranslator;

    //配置授权码服务
    @Bean
    public AuthorizationCodeServices authorizationCodeServices() {

        //授权码服务直接在内存里面
        return new InMemoryAuthorizationCodeServices();
    }

    /**
     * 配置安全约束
     *
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {

        security.tokenKeyAccess("permitAll()")
                .checkTokenAccess("permitAll()")
                //允许表单认证
                .allowFormAuthenticationForClients();
    }

    /**
     * 配置令牌服务
     *
     * @return
     */
    @Bean
    public AuthorizationServerTokenServices tokenServices() {
        DefaultTokenServices services = new DefaultTokenServices();
        //客户端详情服务
        services.setClientDetailsService(clientDetailsService);
        //存储策略
        services.setTokenStore(tokenStore);//必须配置令牌策略
        services.setSupportRefreshToken(true);

        //令牌增强(就是改用其他的令牌加密方式)
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        //设置什么token方式呢
//        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(accessTokenConverter));
//        services.setTokenEnhancer(tokenEnhancerChain);


        List<TokenEnhancer> tokenEnhancers = new ArrayList<>();
        tokenEnhancers.add(tokenEnhancer());
        tokenEnhancers.add(jwtAccessTokenConverter());
        tokenEnhancerChain.setTokenEnhancers(tokenEnhancers);
        services.setTokenEnhancer(tokenEnhancerChain);

        //令牌默认有效期60 * 60 *2 两小时
        services.setAccessTokenValiditySeconds(7200);
        //刷新令牌默认有效时间 60 * 60 *24 * 7 七天
        services.setRefreshTokenValiditySeconds(259200);
        return services;
    }


    /**
     * 客户端信息加载,也可以从数据库加载，我懒得写了
     *
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("client")
                .secret("{bcrypt}" + passwordEncoder.encode("123456"))
                .resourceIds("res1")
                .scopes("all")//授权范围
                .autoApprove(false) //false 跳转授权页面
                .authorizedGrantTypes("client_credentials", "password", "refresh_token", "authorization_code")//四种授权类型
                .redirectUris("http://baidu.com");

    }


    /**
     * 令牌访问端点和授权
     *
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authorizationCodeServices(authorizationCodeServices)//授权码模式需要配置
                .authenticationManager(authenticationManager)//密码模式需要配置
                .tokenServices(tokenServices())//必须配置令牌管理
                .allowedTokenEndpointRequestMethods(HttpMethod.POST)//只允许post请求
                .exceptionTranslator(oauth2ResponseExceptionTranslator); // 设置自定义的异常解析器

    }


    /**
     * 使用非对称加密算法对token签名
     */
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setKeyPair(keyPair());
        return converter;
    }

    /**
     * 从classpath下的密钥库中获取密钥对(公钥+私钥)
     */
    @Bean
    public KeyPair keyPair() {
        KeyStoreKeyFactory factory = new KeyStoreKeyFactory(
                new ClassPathResource("xiong.jks"), "123456".toCharArray());
        KeyPair keyPair = factory.getKeyPair(
                "xiong", "123456".toCharArray());
        return keyPair;
    }

    /**
     * JWT内容增强
     */
    @Bean
    public TokenEnhancer tokenEnhancer() {
        return (accessToken, authentication) -> {
            Map<String, Object> map = new HashMap<>(2);
            User user = (User) authentication.getUserAuthentication().getPrincipal();

            map.put(AuthConstants.JWT_USER_ID_KEY, user.getUid());
            map.put(AuthConstants.JWT_CLIENT_ID_KEY, user.getClientid());
            ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(map);
            return accessToken;
        };
    }
}
